// PatchWindow.m
// By Jayson Adams, NeXT Developer Support Team
// You may freely copy, distribute and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or implied, as to its
// fitness for any particular use.

#import <stdlib.h>
#import <objc/Storage.h>

#import "PatchView.h"

#import "PatchWindow.h"


#define NOVIEWRECT -1

typedef struct _ViewRectPair {
	id	view;
	NXRect	rect;
	} ViewRectPair;


@implementation PatchWindow


/* instance methods */

- initContent:(const NXRect *)contentRect style:(int)aStyle
  backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag
{
    [super initContent:contentRect
	   style:aStyle
	   backing:bufferingType
	   buttonMask:mask
	   defer:flag];

    viewRectList = [[Storage alloc] initCount:0
				    elementSize:sizeof(ViewRectPair)
				    description:"{@ffff}"];
    
    viewRectUnderPoint = NOVIEWRECT;
    
    return self;
}

- registerRect:(NXRect *)rect forView:view
{
    ViewRectPair    *newViewRect;
    
  /* add the view-rect pair to our list */
    newViewRect = (ViewRectPair *)malloc(sizeof(ViewRectPair));
    newViewRect->view = view;
    newViewRect->rect = *rect;
    
    [viewRectList addElement:newViewRect];
    
    return self;
}

- (BOOL)windowEntered:(NXPoint *)mouseLocation fromSource:dragSource
{
    NXPoint	    windowPoint;
    int		    newViewRectUnderPoint;
    ViewRectPair    *viewRect, *oldViewRect;
    BOOL	    oldViewDrew = NO, newViewDrew = NO;
        
  /* convert the mouse location to local coordinates */
    windowPoint = *mouseLocation;
    [self convertScreenToBase:&windowPoint];
    
  /* see if any of the views in our list lay under the mouse point */
    newViewRectUnderPoint = [viewRectList count];
    while (newViewRectUnderPoint--) {
	viewRect = (ViewRectPair *)[viewRectList
      					    elementAt:newViewRectUnderPoint];
	if (NXPointInRect(&windowPoint, &(viewRect->rect))) {
	    break;
	}
    }
    
  /* see if the mouse has moved over a different view */
    if (viewRectUnderPoint != newViewRectUnderPoint) {
      /* tell the view previously under the mouse that the window has exited */
	if (viewRectUnderPoint != NOVIEWRECT) {
	    oldViewRect = (ViewRectPair *)[viewRectList
						 elementAt:viewRectUnderPoint];
	    oldViewDrew = [oldViewRect->view windowExited:dragSource];
	}
      /* save the new view under the mouse */
	viewRectUnderPoint = newViewRectUnderPoint;
	
      /*
       * if there's a view under the mouse point, tell it that a window has
       * entered it
       */
	if (viewRectUnderPoint != NOVIEWRECT) {
	    viewRect = (ViewRectPair *)[viewRectList
	    					elementAt:viewRectUnderPoint];
	    newViewDrew = [viewRect->view windowEntered:dragSource];
	}
    }
    
  /* let the caller know if any of our views has done any drawing */
    return (oldViewDrew || newViewDrew);
}

- (BOOL)windowExited:dragSource
{
    ViewRectPair    *viewRect;
    BOOL	    viewDrew = NO;
    
  /* tell any view previously under the mouse point that it no longer is */
    if (viewRectUnderPoint != NOVIEWRECT) {
	viewRect = [viewRectList elementAt:viewRectUnderPoint];
	viewDrew = [viewRect->view windowExited:dragSource];
	viewRectUnderPoint = NOVIEWRECT;
    }
    
  /* tell the caller whether or not the view did some drawing */
    return viewDrew;
}

- (BOOL)windowDropped:(NXPoint *)mouseLocation fromSource:source
{
    NXPoint	    windowPoint;
    int		    newViewRectUnderPoint;
    ViewRectPair    *viewRect;
    BOOL	    accepted = NO;
    
  /* convert the mouse location to local coordinates */
    windowPoint = *mouseLocation;
    [self convertScreenToBase:&windowPoint];
    
  /* see if any of the views in our list lay under the mouse point */
    newViewRectUnderPoint = [viewRectList count];
    while (newViewRectUnderPoint--) {
	viewRect = (ViewRectPair *)[viewRectList
     					    elementAt:newViewRectUnderPoint];
	if (NXPointInRect(&windowPoint, &(viewRect->rect))) {
	    break;
	}
    }
 
  /* tell any view previously under the mouse point that the window moved */
    if (viewRectUnderPoint != NOVIEWRECT &&
	viewRectUnderPoint != newViewRectUnderPoint) {
	
	viewRect = (ViewRectPair *)[viewRectList elementAt:viewRectUnderPoint];
	[viewRect->view windowExited:source];   
    }
    
  /*
   * tell the view under the mouse point (if any) that the user dropped a
   * window on it
   */
    if (newViewRectUnderPoint != NOVIEWRECT) {
	viewRect = (ViewRectPair *)[viewRectList
      					     elementAt:newViewRectUnderPoint];
	accepted = [viewRect->view windowDropped:source:&windowPoint];
    }
    
  /* no window under the mouse now */
    viewRectUnderPoint = NOVIEWRECT;
    
    return accepted;
}

@end
